package cn.jtfadmin.activiti.controller.rest;

import cn.jtfadmin.activiti.domain.dto.TaskDTO;
import cn.jtfadmin.activiti.service.ActModelService;
import cn.jtfadmin.base.lang.common.domain.dto.PageRDTO;
import cn.jtfadmin.base.lang.common.domain.dto.ResultDTO;
import cn.jtfadmin.base.lang.common.utils.RequestUtils;
import cn.jtfadmin.base.lang.config.user.UserHolder;
import cn.jtfadmin.base.lang.enums.BooleanEnum;
import cn.jtfadmin.core.modules.sys.domain.dto.SysUserModelDTO;
import cn.jtfadmin.core.modules.sys.utils.SysUserDetailUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletInputStream;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流任务controller
 * @author jtf
 * @since 0.0.1
 */
@RestController
public class ActivitiTaskController {

    @Autowired
    private TaskService taskService;

    @Autowired
    private ActModelService actModelService;

    @Autowired
    private ObjectMapper objectMapper;


    /**
     * 获取当前人任务
     * @param modelId 模型id
     * @param tenantId 租户id
     * @param taskName 任务名称
     * @param pageNo 页码
     * @param pageSize 每页数量
     * @return 当前人任务
     */
    @GetMapping(value = "/activiti/task/listCurrentUserTask")
    public ResultDTO<PageRDTO<TaskDTO>> listCurrentUserTask(@RequestParam(required = false) String modelId,
                                                            @RequestParam(required = false) String tenantId,
                                                            @RequestParam(required = false) String taskName,
                                                            @RequestParam(required = false) String processDefinitionName,
                                                            @RequestParam(required = false, defaultValue = "1") Integer pageNo,
                                                            @RequestParam(required = false, defaultValue = "10") Integer pageSize){
        String authenticatedUserId = Authentication.getAuthenticatedUserId();
        if(!StringUtils.hasText(authenticatedUserId)){
            throw new IllegalArgumentException("用户登录失效");
        }
        SysUserModelDTO sysUserModelDTO
                = SysUserDetailUtil.getUserModelDTO(UserHolder.getUserDetails());
        List<String> candidateGroups = new ArrayList<>(StringUtils.commaDelimitedListToSet(sysUserModelDTO.getRoleListCodes()));
        List<String> deploymentIds = null;
        if(StringUtils.hasText(modelId)){
            deploymentIds = new ArrayList<>(actModelService.listDeploymentIdsByModelId(modelId));
        }
        long count = createTaskInstanceQuery(authenticatedUserId, taskName, candidateGroups, tenantId, deploymentIds, processDefinitionName).count();
        List<Task> tasks = createTaskInstanceQuery(authenticatedUserId, taskName, candidateGroups, tenantId, deploymentIds, processDefinitionName)
                .listPage((pageNo - 1) * pageSize, pageSize);
        List<TaskDTO> collect = tasks.stream().map(this::tasksToDTO).collect(Collectors.toList());
        return ResultDTO.successData(new PageRDTO<>(collect,count));
    }



    /**
     * 任务委派
     * @param taskId 任务id
     * @param userId 用户id
     * @return 无
     */
    @PostMapping(value = "/activiti/task/delegate")
    public ResultDTO<String> delegate(String taskId, String userId){
        taskService.delegateTask(taskId, userId);
        return ResultDTO.success();
    }

    /**
     * 任务表单代理完成， 输入流中是任务表单信息
     * @param taskId 任务idid
     * @return
     */
    @PostMapping(value = "/activiti/task/resolve/{taskId}")
    public ResultDTO<String> taskFormResolve(@PathVariable String taskId){


        String formData = null;
        try(ServletInputStream inputStream = RequestUtils.getRequest().getInputStream()){
            formData = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        }catch (IOException e){
            throw new RuntimeException(e);
        }
        Map<String, Object> variables = new HashedMap();
        if(StringUtils.hasText(formData)){
            JsonNode jsonNode = null;
            try {
                jsonNode = objectMapper.readTree(formData);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            Iterator<String> stringIterator = jsonNode.fieldNames();
            while (stringIterator.hasNext()){
                String next = stringIterator.next();
                JsonNode jsonNode1 = jsonNode.get(next);
                String s = jsonNode1.asText("");
                variables.put(next, s);
            }
        }
        taskService.resolveTask(taskId, variables);
        return ResultDTO.success();
    }

    /**
     * 任务表单完成， 输入流中是任务表单信息
     * @param taskId 任务id
     * @return
     */
    @PostMapping(value = "/activiti/task/complate/{taskId}")
    public ResultDTO<String> taskFormComplate(@PathVariable String taskId){


        String formData = null;
        try(ServletInputStream inputStream = RequestUtils.getRequest().getInputStream()){
            formData = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        }catch (IOException e){
            throw new RuntimeException(e);
        }
        Map<String, Object> variables = new HashedMap();
        if(StringUtils.hasText(formData)){
            JsonNode jsonNode = null;
            try {
                jsonNode = objectMapper.readTree(formData);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            Iterator<String> stringIterator = jsonNode.fieldNames();
            while (stringIterator.hasNext()){
                String next = stringIterator.next();
                JsonNode jsonNode1 = jsonNode.get(next);
                String s = jsonNode1.asText("");
                variables.put(next, s);
            }
        }
        taskService.complete(taskId, variables, true);
        return ResultDTO.success();
    }
    /**
     * 删除任务
     * @deprecated 运行中的任务不能删除
     * @param id 任务id
     * @return 无
     */
    @Deprecated
    @DeleteMapping(value = "/activiti/task/del")
    public ResultDTO<String> del(String id){
        taskService.deleteTask(id);
        return ResultDTO.success();
    }

    /**
     * 获取所有任务
     * @param modelId 模型id
     * @param tenantId 租户id
     * @param taskName 任务名称
     * @param username 用户
     * @param processDefinitionName 任务实例名
     * @param pageNo 页码
     * @param pageSize 每页数量
     * @return
     */
    @GetMapping(value = "/activiti/task/listUserTask")
    public ResultDTO<PageRDTO<TaskDTO>> listUserTask(@RequestParam(required = false) String modelId,
                                                     @RequestParam @NotBlank String tenantId,
                                                     @RequestParam(required = false) String taskName,
                                                     @RequestParam(required = false) String username,
                                                     @RequestParam(required = false) String processDefinitionName,
                                                     @RequestParam(required = false, defaultValue = "1") Integer pageNo,
                                                     @RequestParam(required = false, defaultValue = "10") Integer pageSize){
        String authenticatedUserId = username;

        List<String> candidateGroups = new ArrayList<>();
        List<String> deploymentIds = null;
        if(StringUtils.hasText(modelId)){
            deploymentIds = new ArrayList<>(actModelService.listDeploymentIdsByModelId(modelId));
        }
        long count = createTaskInstanceQuery(authenticatedUserId, taskName, candidateGroups, tenantId, deploymentIds, processDefinitionName).count();
        List<Task> tasks = createTaskInstanceQuery(authenticatedUserId, taskName, candidateGroups, tenantId, deploymentIds, processDefinitionName)
                .listPage((pageNo - 1) * pageSize, pageSize);
        List<TaskDTO> collect = tasks.stream().map(this::tasksToDTO).collect(Collectors.toList());
        return ResultDTO.successData(new PageRDTO<>(collect,count));
    }


    private TaskDTO tasksToDTO(Task task) {
        return new TaskDTO(
                task.getId(),
                task.getName(),
                task.getDescription(),
                task.getPriority(),
                task.getOwner(),
                task.getAssignee(),
                task.getProcessInstanceId(),
                task.getExecutionId(),
                task.getProcessDefinitionId(),
                task.getCreateTime(),
                task.getTaskDefinitionKey(),
                task.getDueDate(),
                task.getParentTaskId(),
                task.getFormKey(),
                task.getTenantId(),
                task.getTaskLocalVariables(),
                task.getProcessVariables(),
                task.getClaimTime(),
                task.isSuspended()? BooleanEnum.TRUE:BooleanEnum.FALSE,
                task.getDelegationState()
        );
    }

    private TaskQuery createTaskInstanceQuery(String authenticatedUserId,
                                              String taskName, List<String> candidateGroups,
                                              String taskTenantId, List<String> deploymentIds,
                                              String processDefinitionName){

        TaskQuery taskQuery = taskService.createTaskQuery();

        if(StringUtils.hasText(taskName)){
            taskQuery.taskNameLike(taskName);
        }
        if(StringUtils.hasText(taskTenantId)){
            taskQuery.taskTenantId(taskTenantId);
        }
        if(StringUtils.hasText(processDefinitionName)){
            taskQuery.processDefinitionNameLike(processDefinitionName);
        }
        if(!CollectionUtils.isEmpty(deploymentIds)){
            taskQuery.deploymentIdIn(deploymentIds);
        }

            if(StringUtils.hasText(authenticatedUserId) && !CollectionUtils.isEmpty(candidateGroups)){
                taskQuery.or();
                taskQuery.taskCandidateOrAssigned(authenticatedUserId);
                taskQuery.taskCandidateGroupIn(candidateGroups);
                taskQuery.endOr();
            }else if(StringUtils.hasText(authenticatedUserId) ){
                    taskQuery.taskCandidateOrAssigned(authenticatedUserId);
            }else if(!CollectionUtils.isEmpty(candidateGroups)){
                taskQuery.taskCandidateGroupIn(candidateGroups);
            }



        return taskQuery;
    }
}
